#include "pch.h"
#include "PluginManager.h"
#include "GaojieDriveApp.h"
#include "AppConfig.h"

CPluginManager::CPluginManager()
{
	AssertInUIThread();
}

CPluginManager::~CPluginManager()
{
	AssertInUIThread();
	assert(m_vecPlugins.empty());
}

namespace
{

// Record the mapping of plugin names and exported functions
struct PluginFunc
{
	std::wstring strPluginName;
	bool (*pfCreatePlugin)(IPluginPtr&);
};

IPluginPtr CreatePlugin(std::vector<PluginFunc>& vecPluginFuncs, const std::wstring& strPluginName)
{
	IPluginPtr pPlugin;

	for (const auto& pluginFunc : vecPluginFuncs)
	{
		if (_wcsicmp(pluginFunc.strPluginName.c_str(), strPluginName.c_str()) == 0)
		{
			pluginFunc.pfCreatePlugin(pPlugin);
			return pPlugin;
		}
	}

	// Load the plugin and get the exported function
	for (const auto& pluginCfg : GetApp()->GetAppConfig()->GetPluginCfgs())
	{
		if (_wcsicmp(pluginCfg.strPluginName.c_str(), strPluginName.c_str()) == 0)
		{
			if (HMODULE hModule = LoadLibraryExW(pluginCfg.strPluginDll.c_str(), NULL, LOAD_WITH_ALTERED_SEARCH_PATH))
			{
				PluginFunc pluginFunc = { strPluginName };
				if ((PVOID&)pluginFunc.pfCreatePlugin = GetProcAddress(hModule, "CreatePlugin"))
				{
					pluginFunc.pfCreatePlugin(pPlugin);
					vecPluginFuncs.emplace_back(std::move(pluginFunc));
				}
			}
			break;
		}
	}

	return pPlugin;
}

}	// namespace

bool CPluginManager::Init()
{
	AssertInUIThread();

	bool bRet = true;
	std::vector<PluginFunc> vecPluginFuncs;

	// Load all subdirs and plugins and initialize
	for (const auto& subDirCfg : GetApp()->GetAppConfig()->GetSubDirCfgs())
	{
		IPluginPtr pPlugin = CreatePlugin(vecPluginFuncs, subDirCfg.strPluginName);
		if (!pPlugin)
		{
			LogError(L"Load plugin error: %s", subDirCfg.strPluginName);
			bRet = false;
			break;
		}

		pPlugin->SetServer(subDirCfg.strServerInfo.c_str());
		m_vecPlugins.emplace_back(PluginInfo{ subDirCfg.strSubPath, pPlugin });
	}

	return bRet;
}

void CPluginManager::Exit()
{
	AssertInUIThread();

	m_vecPlugins.clear();
}

// Find the plugin belonging to strFilePath
size_t CPluginManager::GetPlugin(const std::wstring_view& strFilePath, IPluginPtr& pPlugin) const
{
	for (const auto& pluginInfo : m_vecPlugins)
	{
		const auto& strSubPath = pluginInfo.strSubPath;

		// strSubPath is on strFilePath
		if (strSubPath.size() <= strFilePath.size() && _wcsnicmp(strSubPath.c_str(), strFilePath.data(), strSubPath.size()) == 0)
		{
			pPlugin = pluginInfo.pPlugin;
			return strSubPath.size();
		}
	}

	return 0;
}
